package com.example.demo.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping(value = "test")
public class MsService {

    @Autowired
    private RedisService redisService;


    //锁超时时间，防止线程在入锁以后，无限的执行等待
    private int expireMsecs = 30 * 1000;
    //锁等待时间，防止线程饥饿
    private int timeoutMsecs = 10 * 1000;
    private volatile boolean locked = false;
    //设置锁的唯一key
    String lockKey = "good";
    //模拟初始商品数
    int n = 50;

    /***
     * 抢购代码
     * @return
     */
    @RequestMapping(value = "/thread",method = RequestMethod.GET)
    public boolean seckill() {
        try {
            if(n<=0) {
                System.out.println("手慢拍大腿,抢光啦222222222！");
                return false;
            }
            if (this.lock()) {
                //修改库存
                if(n-1>=0) {
                    n--;
                    System.out.println("库存数量:"+n+"     成功!!!"+Thread.currentThread().getName() + "获得了锁");
                }else {
                    System.out.println("手慢拍大腿,抢光啦！");
                    return false;
                }
                this.unlock();
                return true;
            }else System.out.println("换个姿势再试试");


        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 为了让分布式锁的算法更稳键些，持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时，再去做DEL操作，因为可能客户端因为某个耗时的操作而挂起，
            // 操作完的时候锁因为超时已经被别人获得，这时就不必解锁了。 ————这里没有做
            this.unlock();
        }
        return false;
    }




    public synchronized boolean lock() throws InterruptedException {

            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); // 锁到期时间
            if (redisService.setnxByKey(lockKey, expiresStr).toString().equals("1")) {
                // lock acquired
                locked = true;
                return true;
            }

            String currentValueStr = redisService.getByKey(lockKey).toString(); // redis里的时间
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                // 判断是否为空，不为空的情况下，如果被其他线程设置了值，则第二个条件判断是过不去的
                // lock is expired

                String oldValueStr = redisService.getSetByKey(lockKey, expiresStr);
                // 获取上一个锁到期时间，并设置现在的锁到期时间，
                // 只有一个线程才能获取上一个线上的设置时间，因为jedis.getSet是同步的
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    // 防止误删（覆盖，因为key是相同的）了他人的锁——这里达不到效果，这里值会被覆盖，但是因为什么相差了很少的时间，所以可以接受

                    // [分布式的情况下]:如过这个时候，多个线程恰好都到了这里，但是只有一个线程的设置值和当前值相同，他才有权利获取锁
                    // lock acquired
                    locked = true;
                    return true;
                }
            }
        return false;
    }





    /**
     * Acqurired lock release.
     */
    public synchronized void unlock() {
        if (locked) {
            redisService.del(lockKey);
            locked = false;
        }
    }
}
